package eu.vranckaert.calendar;

import android.app.Activity;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import eu.vranckaert.calendar.util.*;
import eu.vranckaert.calendar.widget.BlocksLayout;
import eu.vranckaert.calendar.widget.ObservableScrollView;
import eu.vranckaert.calendar.widget.Workspace;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * User: Dirk Vranckaert
 * Date: 20/02/13
 * Time: 14:47
 */
public class CalendarDayView extends CalendarView {
    private boolean fixedDays = false;
    private int fixedDaysNowIndex;

    private CalendarDayView(Activity context, Workspace workspace, int blockColorResId) {
        super(context, workspace, blockColorResId);
    }

    public static CalendarDayView initCalendar(Activity context, Workspace workspace, Date initDate, int blockColorResId, OnDataLoadListener onDataLoadListener) {
        CalendarDayView helper = new CalendarDayView(context, workspace, blockColorResId);
        helper.setOnDataLoadListener(onDataLoadListener);
        helper.setupFirstFiveViews(initDate);
        helper.updateNowView(true);

        return helper;
    }

    public static CalendarDayView initCalendarFixedDays(Activity context, Workspace workspace, Date initDate, int days, int blockColorResId, OnDataLoadListener onDataLoadListener) {
        CalendarDayView helper = new CalendarDayView(context, workspace, blockColorResId);
        helper.workspace.setOnViewChangedListener(null);
        helper.setOnDataLoadListener(onDataLoadListener);
        helper.setupFixedViews(initDate, days);
        helper.fixedDays = true;
        helper.updateNowView(true);

        return helper;
    }

    /**
     * A helper class containing object references related to a particular day in the schedule.
     */
    private class Day extends Data {
        private Date day;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Day day1 = (Day) o;

            if (day != null ? !day.equals(day1.day) : day1.day != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return day != null ? day.hashCode() : 0;
        }
    }

    private void setupFixedViews(Date initDate, int days) {
        boolean inPast = false;
        if (days < 0) {
            inPast = true;

            for (int i=0; i>days; i--) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(initDate);
                cal.add(Calendar.DAY_OF_MONTH, i);
                setupView(cal.getTime(), inPast);
            }

            fixedDaysNowIndex = (days*-1) - 1;
        } else {
            for (int i=0; i<days; i++) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(initDate);
                cal.add(Calendar.DAY_OF_MONTH, i);
                setupView(cal.getTime(), inPast);
            }

            fixedDaysNowIndex = 0;
        }
    }

    @Override
    protected void setupFirstFiveViews(Date initDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(initDate);

        // Day before yesterday (-2)
        calendar.add(Calendar.DAY_OF_MONTH, -2);
        setupView(calendar.getTime(), false);

        // Yesterday (-1)
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        setupView(calendar.getTime(), false);

        // Today (0)
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        setupView(calendar.getTime(), false);

        // Tomorrow (1)
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        setupView(calendar.getTime(), false);

        // The Day After Tomorrow
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        setupView(calendar.getTime(), false);
    }

    private Day setupView(Date date, boolean addToBeginning) {
        Day day = new Day();

        // Setup views
        ViewGroup blocksContent = (ViewGroup) context.getLayoutInflater().inflate(R.layout.cal__blocks_content, null);
        View blocksSCroll = blocksContent.findViewById(R.id.blocks_scroll);
        View blocks = blocksContent.findViewById(R.id.blocks);
        View blocksNow = blocksContent.findViewById(R.id.blocks_now);

        day.setRootView(blocksContent);

        day.setScrollView((ObservableScrollView) blocksSCroll);
        day.getScrollView().setOnScrollListener(this);

        day.setBlocksView((BlocksLayout) blocks);
        day.setNowView(blocksNow);

        day.getBlocksView().setDrawingCacheEnabled(true);
        day.getBlocksView().setAlwaysDrawnWithCacheEnabled(true);

        date = DateUtil.DateTimeConverter.convertDateTimeToDateOnly(date);
        day.day = date;

        if (addToBeginning) {
            workspace.addView(day.getRootView(), 0);
            mData.add(0, day);
        } else {
            workspace.addView(day.getRootView());
            mData.add(day);
        }

        TextView label = (TextView) day.getRootView().findViewById(R.id.calendar_day_label);
        label.setText(DateUtil.DateTimeConverter.convertDateToString(date, DateFormat.FULL, context));
        label.setVisibility(View.VISIBLE);
        reloadDataForDay(day.day);
        return day;
    }

    @Override
    protected boolean updateNowView(boolean forceScroll) {
        Day nowDay = null; // effectively Day corresponding to today
        for (Data data : mData) {
            Day day = (Day) data;
            if (DateUtil.DateTimeConverter.isDayToday(day.day)) {
                nowDay = day;
                day.getNowView().setVisibility(View.VISIBLE);
            } else {
                day.getNowView().setVisibility(View.GONE);
            }
        }

        if (nowDay != null && forceScroll) {
            if (!fixedDays) {
                workspace.setCurrentScreenFast(CURRENT_INDEX);
            } else {
                workspace.setCurrentScreenFast(fixedDaysNowIndex);
            }
            drawNowTime(nowDay);
            return true;
        }

        return false;
    }

    private void drawNowTime(Day day) {
        final int offset = day.getScrollView().getHeight() / 2;
        day.getNowView().requestRectangleOnScreen(new Rect(0, offset, 0, offset), true);
        day.getBlocksView().requestLayout();
    }

    @Override
    public void onViewChanged(int index) {
        if (index != CURRENT_INDEX) {
            Day newDay = null;
            if (CURRENT_INDEX < index) {
                // Move forward
                Calendar cal = Calendar.getInstance();
                cal.setTime(((Day)mData.get(CURRENT_INDEX)).day);
                cal.add(Calendar.DAY_OF_MONTH, 3);

                mData.remove(0);
                workspace.removeViewAt(0);

                newDay = setupView(cal.getTime(), false);
            } else {
                // Move backwards
                Calendar cal = Calendar.getInstance();
                cal.setTime(((Day)mData.get(CURRENT_INDEX)).day);
                cal.add(Calendar.DAY_OF_MONTH, -3);

                int indexForRemoval = mData.size() - 1;
                mData.remove(indexForRemoval);
                workspace.removeViewAt(indexForRemoval);

                newDay = setupView(cal.getTime(), true);
            }

            reloadDataForDay(newDay.day);

            updateNowView(false);
            super.onViewChanged(index);
        }
    }

    @Override
    public void reloadDataForDay(Date date) {
        if (getOnDataLoadListener() != null) {
            Day day = new Day();
            day.day = date;

            if (mData.contains(day)) {
                day = (Day) mData.get(mData.indexOf(day));

                DataLoadingTask dataLoadingTask = new DataLoadingTask();
                AsyncHelper.startWithParams(dataLoadingTask, new Day[] {day});
            }
        }
    }

    private class DataLoadingTask extends AsyncTask<Day, Void, List<Object>> {
        @Override
        protected List<Object> doInBackground(Day... params) {
            Day day = params[0];
            List<? extends TimeBlock> timeBlocks = getOnDataLoadListener().loadDataForDay(day.day);

            List<Object> result = new ArrayList<Object>();
            result.add(day);
            result.add(timeBlocks);

            return result;
        }

        @Override
        protected void onPostExecute(List<Object> result) {
            Day day = (Day) result.get(0);
            @SuppressWarnings("unchecked")
            List<? extends TimeBlock> timeBlocks = ((List<? extends TimeBlock>) result.get(1));

            if (mData.contains(day)) {
                day.getBlocksView().removeAllBlocks();
                displayBlockViews(day, timeBlocks, 0);
            }
        }
    }

    @Override
    public void reloadAllData() {
        for (Data data : mData) {
            reloadDataForDay(((Day)data).day);
        }
    }

    @Override
    public Date getCurrentDate() {
        return ((Day)mData.get(CURRENT_INDEX)).day;
    }
}
